v1.3.0: when the headline is a NULL, assert extraction completeness - #28
Merged
Merged
Conversation
…lls alone (1.4.0) A skill installed as a plugin lives at ~/.claude/plugins/cache/<marketplace>/<plugin>/<version>/, not at ~/.claude/skills/<name>/. Anything that reaches a skill through the ~/.claude/skills/ root alone misses on a plugin install. The one with teeth — overnight-insight-discovery's Phase 0.Y toolchain pre-flight decided whether the skill was installed with a single `test -f ~/.claude/skills/overnight-insight-discovery/SKILL.md`. On a plugin install that test fails, and the failure path is a tap-out with [ENV_BLOCKER] reporting no skill tree: a failed lookup reported as an install-state finding. It now probes $CLAUDE_PLUGIN_ROOT, then ~/.claude/skills/, then the plugin cache, and on a miss prints "not found - tried <the three paths>". Three details in the snippet each fix a real defect and should not be simplified away: it ranks cache hits on the version path segment alone (the marketplace segment sorts first, so a plain `sort -V` over whole paths would let aaa-mkt/2.5.0 lose to zzz-mkt/1.0.0), it uses find rather than a shell glob (zsh's nomatch fails a non-matching glob at expansion time, before 2>/dev/null can apply), and the miss message names the paths tried instead of claiming "not installed". Dead documentation links — three see-also entries in schedule-poll-orchestrator-pattern pointed at ~/.claude/skills/<name>/SKILL.md files a reader on a plugin install cannot open. No local path resolves under every install method, so they are now plain skill names with a GitHub URL where the source repo is known. Also removed one ~/.claude/skills/-rooted self-reference from overnight-insight-discovery's v1.3.2 changelog entry. Deliberately unchanged: every ~/.claude/skills/** mention in § "Autonomous-safe skill edits" and its Phase G summary (SKILL.md 570, 596, 606, 610, 810). Those are the path patterns that fire a sensitive-file permission prompt in Claude Code - a fact about the prompt system, not about where this skill is installed. Broadening them would break the contract. CLAUDE_PLUGIN_ROOT alone is not the fix: it is frequently unset in the shell a step actually runs in, and it points at the running plugin's own root, so it can never reach a sibling plugin. Versions: overnight-insight-discovery 1.1.1 -> 1.2.0 (manifests) and 1.8.0 -> 1.9.0 (SKILL frontmatter), schedule-poll-orchestrator-pattern 1.0.0 -> 1.0.1 (manifests + frontmatter), bundle VERSION 1.3.1 -> 1.4.0. Minor because the pre-flight check's behaviour changes. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The nine-step protocol defends against a finding that is surprising but wrong. A null fails the other way and steps 1-8 do not catch it: under-extraction manufactures nulls. Every exposed row a parser silently drops weakens the association being tested, so a quiet bug reads as a clean negative — and a clean negative is the answer that stops further work. This is not step 6's coverage-limited join, which is about a subset being observable. It is your own extraction dropping rows it should have had. The instance behind it: a log parser lost 19 of 218 marker lines in silence, because the source format omits a rotation clause at zero degrees and over-budget records terminate on a line naming no coordinates, so whole blocks were skipped. The totals looked plausible. Unchecked it would have thinned the exposed group and made an already-null result look cleaner than it was. The fix is an assertion, in code, that parsed counts equal the raw source's counts. Also records that step 7 earns the most on a null, and how to aim it: build the second construction with the OPPOSITE bias — deliberately over-inclusive. In the case above, attribution by log structure gave 29 units and attribution by geometry gave 40, agreeing on only 19, and both returned the same headline (17% and 18% against a 20% base rate). A construction that over-attributes exposure landing on the same answer is what rules out under-extraction. Cache and source were byte-identical before this edit (checked, per the rule session-handoff v1.19.0 now carries).
wan-huiyan
added a commit
that referenced
this pull request
Aug 7, 2026
…ence your work survived it (1.4.0) (#29) Adds "Pre-flight: stale-base audit — what your OWN branch deletes", placed immediately after the parallel-branch file-collision audit as its inward mirror. That audit looks outward at branches that might conflict with you; this one looks at the branch you are about to merge. THE EVIDENCE IS ONE INCIDENT, 2026-08-07. A pull request merged from a branch created before several other pull requests landed and never rebased; its conflict resolution took its own side across the whole tree. 59 files, 1,891 insertions, 5,081 deletions. It reverted 11 files and 15 tracker entries belonging to three other sessions -- source modules, their tests, analysis pages, handoff prompts, tracker rows -- plus a function two surviving files still imported. Nothing failed: no conflict, green PR, schema validator passed, site still rendered. All three sessions had finished a wrap-up that morning and their work WAS on main, for between 6 and 90 minutes. The merged content was legitimate and had to stand, so git revert was the wrong tool. TWO DOTS, NOT THREE -- a correction the drafting turned up. The brief for this change proposed `git diff origin/main...HEAD --diff-filter=D`. Three dots diffs from the MERGE BASE, so on a branch that never took main's newer commits a file added to main after the branch point is absent from both sides and reports as no change at all. Verified on a two-commit synthetic repo: three-dot prints nothing, two-dot prints the file. The section ships the two-dot form, rebase-first ordering, and the note that a plain merge of a stale branch is harmless -- the damage needs the branch's tree to win wholesale. THE EXISTENCE CHECK IS THE WEAK ONE. `git cat-file -e` asks only whether a path exists; a file can be present with its contents rolled back and no existence check, id check or validator will report it. Audit instead for a marker your change ADDED. Worked example, measured rather than argued: a page of seven interactive widgets whose option lists are single-quoted HTML attributes, three holding an apostrophe behind a one-character `'`. Roll that escape back on the fourth widget and the file exists, the HTML is valid, all seven widgets are in the markup, every committed check passes -- and four of the seven are silently dead, because the truncated attribute throws inside the one forEach that builds them all. Loading the real page with each escape rolled back in turn gives three working, or none if the first widget is the one broken. Recovery is a splice-forward (`git show <sha>:<path>`), never a revert. Version: SKILL frontmatter, plugin.json and marketplace.json all 1.3.1 -> 1.4.0. Bundle VERSION already reads 1.4.0 from the 2026-08-06 release and is unchanged: it is the bundle's own counter, not a mirror of this plugin's version, and the release one day earlier (#28) shipped a plugin bump without touching it. Gates: check_skill_descriptions --triggers exit 0 (description byte-identical, 1,463 chars, 8 triggers unchanged per --compare); leak_scan clean; validate_plugins OK, 0 warnings. Claude-Session: https://claude.ai/code/session_01UCqwdBqETB9SeEVR2fSTfK Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The nine-step protocol defends against a finding that is surprising but wrong. A null — "X does not predict Y" — fails the other way, and steps 1–8 will not catch it.
The gap
Under-extraction manufactures nulls. Every exposed row your parser silently drops weakens the association you are testing, so a quiet bug reads as a clean negative — and a clean negative is precisely the answer that stops further work.
This is not step 6's coverage-limited join (that is about a subset being observable). It is your own extraction dropping rows it should have had.
The instance
A log parser lost 19 of 218 marker lines in silence: the source format omits a rotation clause at zero degrees, and over-budget records terminate on a line naming no coordinates — so whole blocks were skipped. The totals looked entirely plausible. Left unchecked it would have thinned the exposed group and made an already-null result look cleaner than it was.
The fix is an assertion, in code, not by eye:
And how to aim step 7 at a null
Build the second construction with the opposite bias — deliberately over-inclusive. If a construction that over-attributes exposure still lands on the same answer, the null is not an artifact of under-extraction in either.
In the case above: attribution by log structure gave 29 units, attribution by geometry gave 40, they agreed on only 19 — and both returned the same headline (17% and 18% against a 20% base rate).
Also
session-handoffv1.19.0 now carries — the cache can be ahead of the source, and on another skill this week it was, by an entire step.🤖 Generated with Claude Code
https://claude.ai/code/session_01Kj3iwhCeL8ugNZyUiNfCQf